package ru.bragnikita.utils.swing.widget.common;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.ListIterator;

/**
 * @author Nikita.Brazhnikov
 */
public abstract class EventSupport<T, V> implements IsEventSupport<T,V> {

    private List<WeakReference<T>> listeners = Lists.newLinkedList();

    @Override
    public synchronized boolean addListener(T listener) {
        for (WeakReference<T> weakReference : listeners) {
            if (listener == weakReference.get())
                return false;
        }
        listeners.add(new WeakReference<T>(listener));
        return true;
    }

    @Override
    public synchronized boolean removeListener(final T listener) {
        WeakReference<T> ref = Iterables.find(listeners, new Predicate<WeakReference<T>>() {
            @Override
            public boolean apply(@Nullable WeakReference<T> input) {
                return input != null && listener == input.get();
            }
        }, null);
        return ref != null && listeners.remove(ref);
    }

    @Override
    public synchronized void fireEvent(V event) {
        ListIterator<WeakReference<T>> iterator = listeners.listIterator();
        if (iterator.hasNext()) {
            WeakReference<T> listenerRef = iterator.next();
            T listener = listenerRef.get();
            if (listenerRef.isEnqueued()) {
                iterator.remove();
            } else
            try {
                listenerCall(listener, event);
            } catch (Exception e) {
                onException(e, event, listener);
            }
        }
    }

    protected abstract void listenerCall(T listener, V event);

    protected void onException(Exception e, V event, T listener) {

    }

}
